#include "key_helper.h"
#include "utils.h"
#include "uvlog.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <arpa/inet.h>

const unsigned char UV_UCHAR_MIN = 0;
const unsigned char UV_UCHAR_MAX = 0XFF;
const unsigned int UV_UINT_MIN = 0;
const unsigned int UV_UINT_MAX = 0XFFFFFFFF;
const uint64_t UV_UINT64_MIN = 0;
const uint64_t UV_UINT64_MAX = 0XFFFFFFFFFFFFFFFF;
const unsigned int UV_MD5_MIN[] =  {0,0,0,0};
const unsigned int UV_MD5_MAX[] =  {0XFFFFFFFF,0XFFFFFFFF,0XFFFFFFFF,0XFFFFFFFF};

#define MAX_KEY_COUNT 10
#define MINMAX_CPY(dest,len,type) switch(len){case UINT: memcpy(dest,&UV_UINT_##type,len);break;\
	case UCHAR: memcpy(dest,&UV_UCHAR_##type,len);break;\
	case UINT64: memcpy(dest,&UV_UINT64_##type,len);break;\
	case MD5: memcpy(dest,&UV_MD5_##type,len);break;}



typedef struct{
	int v[4];
}T_MD5;

KeyHelper::KeyHelper()
{
	keyLen = 0;
	keyCount = 0;
	keys = NULL;
}

KeyHelper::~KeyHelper(){
	if(keys){
		delete[] keys;
		keys = NULL;
	}
}


int KeyHelper::init(const char* config){
	keyLen = 0;
	keyCount = 0;

	if(keys == NULL)
		keys = new KEYTYPE[10];
	
	int len = strlen(config);
	char* buf = new char[len+2];
	char* pbuf = buf;
	
	strcpy(buf,config);
	if(buf[len-1] != '|'){
		buf[len] = '|';
		buf[len+1]=0;
		++len;
	}

	for(int i = 0; i<len;++i){
		buf[i] = toupper(buf[i]);
	}

	while(isspace(*buf)){
		++buf;
	}
	
	char *p1 = buf;
	char *p2;

	while((p2 = strchr(p1,'|'))){
		buf = p2+1;
		*p2 = 0;
		while(p2 - 1 > p1 && isspace(*(p2-1))){
			*(--p2) = 0;
		}

		if(strcmp(p1,"UCHAR") == 0){
			keys[keyCount] = UCHAR;
			keyLen += 1;
			++keyCount;
		}
		else if(strcmp(p1,"UINT") == 0){
			keys[keyCount] = UINT;
			keyLen += 4;
			++keyCount;
		}
		else if(strcmp(p1,"UINT64") == 0){
			keys[keyCount] = UINT64;
			keyLen += 8;
			++keyCount;
		}
		else if(strcmp(p1,"MD5") == 0){
			keys[keyCount] = MD5;
			keyLen += 16;
			++keyCount;
		}
		else{

			delete [] pbuf;
			pbuf = NULL;
			return -1;
		}

		if(keyCount > MAX_KEY_COUNT){
			delete [] pbuf;
			pbuf = NULL;
			return -1;
		}
		
		if(!*buf){
			break;
		}

		while(*buf && isspace(*buf)){
			*(buf++) = 0;
		}
	
		
		p1 = buf;
	}

	delete [] pbuf;
	pbuf = NULL;
	return 0;
}

int KeyHelper::compare(const void* k1,const void* k2){
	int pos = 0;
	for(int i = 0; i < keyCount; ++i){
		if(keys[i] == KeyHelper::UINT){
			unsigned int* v1 = (unsigned int*)((char*)k1+pos);
			unsigned int* v2 = (unsigned int*)((char*)k2+pos);

			pos += 4;

			 if(*v1 != *v2){
				if(*v1 > *v2) return 1;
				return -1;
			 }
		}
		else if(keys[i] == KeyHelper::UCHAR){
			unsigned char* v1 = (unsigned char*)((char*)k1+pos);
			unsigned char* v2 = (unsigned char*)((char*)k2+pos);

			pos += 1;
			
			if(*v1 != *v2){
				return *v1 - *v2;
			 }
		}
		else if(keys[i] == KeyHelper::UINT64){
			uint64_t* v1 = (uint64_t*)((char*)k1+pos);
			uint64_t* v2 = (uint64_t*)((char*)k2+pos);

			pos += 8;

			 if(*v1 != *v2){
				if(*v1 > *v2) return 1;
				return -1;
			 }
		}
		else if(keys[i] == KeyHelper::MD5){
			int ret = memcmp((char*)k1+pos,(char*)k2+pos,16);

			pos += 16;
			
			if(ret != 0){
				return ret;
			}
		}
	}

	return 0;
}

char* KeyHelper::format(const void* k,char* buf){
	int pos = 0;
	int bp = 0;
	for(int i = 0; i < keyCount; ++i){
		if(keys[i] == KeyHelper::UINT){
			
			int* v = (int*)((char*)k+pos);		
			bp += sprintf(buf+bp,"%u",*v);

			pos += 4;
		}
		else if(keys[i] == KeyHelper::UCHAR){
			
			char* v = (char*)((char*)k+pos);		
			bp += sprintf(buf+bp,"%u",*v);
			
			pos += 1;		
		}
		else if(keys[i] == KeyHelper::MD5){
			/*
			for(int j = 0; j < 4;++j){
				int* v = (int*)((char*)k+pos+j*4);		
				bp += sprintf(buf+bp,"%08X",*v);
			}
			*/
			for(int j=0;j<16;j++)
			{
				char* v = (char*)k+pos+j;		
				bp += sprintf(buf+bp,"%02X",(unsigned char)*v);
			}
			
			pos += 16;	
		}
		else if(keys[i] == KeyHelper::UINT64){

			uint64_t* v = (uint64_t*)((char*)k+pos);		
			bp += sprintf(buf+bp,"%lu",*v);

			pos += 8;
		}
		
		if(i+1 < keyCount){
			bp += sprintf(buf+bp,"-");
		}
	}
	return buf;
}


void* KeyHelper::ntoh(void* input){
	int pos = 0;
	for(int i = 0; i < keyCount; ++i){
		if(keys[i] == KeyHelper::UINT){
			int* v = (int*)((char*)input+pos);

			*v = ntohl(*v);
			 
			pos += 4;
		}
		else if(keys[i] == KeyHelper::UCHAR){			
			
			pos += 1;
		}
		else if(keys[i] == KeyHelper::UINT64){
			uint64_t* v = (uint64_t*)((char*)input+pos);

			*v = Utils::ntoh64(*v);
			 
			pos += 8;
		}
		else if(keys[i] == KeyHelper::MD5){
			/*
			int* v = (int*)((char*)input+pos);
			
			for(int j = 0 ; j < 4;++j){



				

				*v = ntohl(*v);
				 
				pos += 4;
			}

			int tmp = v[0];
			v[0] = v[3];
			v[3] = tmp;

			tmp = v[1];
			v[1] = v[2];
			v[2] = tmp;
			*/
			pos += 16;
		}
	}

	return input;
}

void* KeyHelper::ntohEx(void* input){
	int pos = 0;
	for(int i = 0; i < keyCount - 1; ++i){
		if(keys[i] == KeyHelper::UINT){
			int* v = (int*)((char*)input+pos);

			*v = ntohl(*v);
			 
			pos += 4;
		}
		else if(keys[i] == KeyHelper::UCHAR){			
			
			pos += 1;
		}
		else if(keys[i] == KeyHelper::UINT64){
			uint64_t* v = (uint64_t*)((char*)input+pos);

			*v = Utils::ntoh64(*v);
			 
			pos += 8;
		}
		else if(keys[i] == KeyHelper::MD5){
			/*
			int* v = (int*)((char*)input+pos);
			
			for(int j = 0 ; j < 4;++j){
				int* v = (int*)((char*)input+pos);

				*v = ntohl(*v);
				 
				pos += 4;
			}

			int tmp = v[0];
			v[3] = v[0];
			v[0] = tmp;

			tmp = v[1];
			v[2] = v[1];
			v[1] = tmp;
			*/

			pos +=16;
		}
	}

	return input;
}


void* KeyHelper::ntohPoi(void *input)
{
	return htonPoi(input);
}

void* KeyHelper::htonPoi(void *input)
{
	int pos = 0;
	for(int i = 0; i < keyCount - 1; ++i){
		if(keys[i] == KeyHelper::UINT){
			int* v = (int*)((char*)input+pos);

			*v = ntohl(*v);
			 
			pos += 4;
		}
		else if(keys[i] == KeyHelper::UCHAR){			
			
			pos += 1;
		}
		else if(keys[i] == KeyHelper::UINT64){
			uint64_t* v = (uint64_t*)((char*)input+pos);

			*v = Utils::ntoh64(*v);
			 
			pos += 8;
		}
		else if(keys[i] == KeyHelper::MD5){
			/*
			int* v = (int*)((char*)input+pos);
			
			for(int j = 0 ; j < 4;++j){



				

				*v = ntohl(*v);
				 
				pos += 4;
			}

			int tmp = v[0];
			v[0] = v[3];
			v[3] = tmp;

			tmp = v[1];
			v[1] = v[2];
			v[2] = tmp;
			*/
			pos += 16;
		}
	}

	return input;
}

void* KeyHelper::hton(void* input){
	return ntoh(input);
}


template<typename T>
static bool isStatisfy(T k1,T k2,char con){
	switch(con){
	case KeyHelper::NO:
		return true;
	case KeyHelper::LE:
		return k1 <= k2 ;
	case KeyHelper::LT:
		return k1 < k2 ;
	case KeyHelper::EQ:
		return k1 == k2;
	case KeyHelper::GE:
		return k1 >= k2;
	case KeyHelper::GT:
		return k1 > k2;
	}

	return false;
}

bool KeyHelper::isConditionStatisfy(const void* k1,const void* k2,const char* condition)
{
	int pos = 0;
	for(int i = 0; i < keyCount; ++i){
		char con = condition[i] & 0X7F;

		if(keys[i] == KeyHelper::UINT){
			unsigned int* v1 = (unsigned int*)((char*)k1+pos);
			unsigned int* v2 = (unsigned int*)((char*)k2+pos);

			pos += 4;

			 if(!isStatisfy(*v1,*v2,con)){
				return false;
			 }
		}
		else if(keys[i] == KeyHelper::UCHAR){
			unsigned char* v1 = (unsigned char*)((char*)k1+pos);
			unsigned char* v2 = (unsigned char*)((char*)k2+pos);

			pos += 1;
			
			if(!isStatisfy(*v1,*v2,con)){
				return false;
			 }
		}
		else if(keys[i] == KeyHelper::UINT64){
			uint64_t* v1 = (uint64_t*)((char*)k1+pos);
			uint64_t* v2 = (uint64_t*)((char*)k2+pos);

			pos += 8;

			 if(!isStatisfy(*v1,*v2,con)){
				return false;
			 }
		}
		else if(keys[i] == KeyHelper::MD5){
			int ret = memcmp((char*)k1+pos,(char*)k2+pos,16);

			pos += 16;
			
			switch(con){
			case KeyHelper::EQ:
				if(ret != 0){
					return false;
				}
				break;
			case KeyHelper::NO:
				break;
			case KeyHelper::LE:
				if(ret > 0){
					return false;
				}
				break;
			case KeyHelper::LT:
				if(ret >= 0){
					return false;
				}
				break;
			case KeyHelper::GE:
				if(ret < 0){
					return false;
				}
				break;
			case KeyHelper::GT:
				if(ret <= 0){
					return false;
				}
				break;
			}
		}
		
	}

	return true;
}


int KeyHelper::conditionAnalyse(const void*key,const char* condition,void* keyLeft,void* keyRight)//,int& direction)
{
	int i,pos = 0;
	for(i = 0; i < keyCount ; ++i){
		char con = condition[i] & 0X7F;
		switch(con){
		case KeyHelper::EQ:
			memcpy((char*)keyLeft+pos,(char*)key+pos,keys[i]);	
			memcpy((char*)keyRight+pos,(char*)key+pos,keys[i]);		
			break;
		case KeyHelper::NO:
			MINMAX_CPY((char*)keyLeft+pos, keys[i], MIN);
			MINMAX_CPY((char*)keyRight+pos, keys[i], MAX);			
			break;
		case KeyHelper::LE :
		case KeyHelper::LT :
			MINMAX_CPY((char*)keyLeft+pos, keys[i], MIN);
			memcpy((char*)keyRight+pos,(char*)key+pos,keys[i]);		
			break;
		case KeyHelper::GE:
		case KeyHelper::GT:
			memcpy((char*)keyLeft+pos,(char*)key+pos,keys[i]);	
			MINMAX_CPY((char*)keyRight+pos,keys[i],MAX);
			break;
		}

		pos += keys[i];
	}

	//char con = condition[i-1] & 0X7F;
	/*
	if(condition[i] & 0X80){
		direction = KeyHelper::LEFT;
		LOG_DEBUG("search direction left");
	}
	else{
		direction= KeyHelper::RIGHT;
		LOG_DEBUG("search direction right");
	}
	*/
	
	return 0;
/*
	int i,pos = 0;
	for(i = 0; i < keyCount - 1; ++i){
		if(condition[i] == KeyHelper::EQ){
			memcpy((char*)keyLeft+pos,(char*)key+pos,keys[i]);	
			memcpy((char*)keyRight+pos,(char*)key+pos,keys[i]);

			pos += keys[i];
		}
		else
		{
			return -1;
		}
	}

	char con = condition[i] & 0X7F;
	if(condition[i] & 0X80){
		direction = KeyHelper::LEFT;
		LOG_DEBUG("search direction left");
	}
	else{
		direction= KeyHelper::RIGHT;
		LOG_DEBUG("search direction right");
	}

	LOG_DEBUG("condition=%u",con);
	
	switch(con){
	case KeyHelper::EQ:
		memcpy((char*)keyLeft+pos,(char*)key+pos,keys[i]);	
		memcpy((char*)keyRight+pos,(char*)key+pos,keys[i]);		
		break;
	case KeyHelper::NO:
		MINMAX_CPY((char*)keyLeft+pos, keys[i], MIN);
		MINMAX_CPY((char*)keyRight+pos, keys[i], MAX);			
		break;
	case KeyHelper::LE :
	case KeyHelper::LT :
		MINMAX_CPY((char*)keyLeft+pos, keys[i], MIN);
		memcpy((char*)keyRight+pos,(char*)key+pos,keys[i]);		
		break;
	case KeyHelper::GE:
	case KeyHelper::GT:
		memcpy((char*)keyLeft+pos,(char*)key+pos,keys[i]);	
		MINMAX_CPY((char*)keyRight+pos,keys[i],MAX);
		break;
	}

	pos += keys[i];

	return 0;
*/	
}

